/**
 *  mailbox.c - Related routines of mailbox.
 *
 *  Copyright (C) 2008-2009 ZhangHu
 *  All rights reserved.
 *  E-MAIL: anmnmnly@gmail.com
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "include/mailbox.h"



/**
 * mboxInit - It creates a new mailbox.
 * @Size: The size of mailbox.
 * @return: The start address of new mailbox.
 *
 * @notes:
 */
mail_t *mboxInit(uword_t Size) {
    mail_t *pmail = NULL;
    msg_t *pmsg = NULL;

    pmail = (mail_t*)kmalloc(sizeof(mail_t));
    if(pmail == NULL) {
        return(NULL);
    }

    pmsg = (msg_t*)kmalloc(Size *sizeof(msg_t));
    if(pmsg == NULL) {
        kfree(pmail);
        return(NULL);
    }
    kmemset(pmail, 0, sizeof(mail_t));
    kmemset(pmsg, 0, Size * sizeof(msg_t));

    pmsg->key_id = 0;
    pmsg->rev_id = 0;
    pmsg->send_id = 0;
    pmsg->pmsg = NULL;

    pmail->size = Size;
    pmail->used = 0;
    pmail->start = pmsg;
    pmail->end = &pmsg[Size - 1];
    pmail->pos = pmsg;
    return(pmail);
}



/**
 * mboxSend - Sending a message to a mailbox.
 * @pMial: Pointer to the mailbox.
 * @Key: Serial number of message.
 * @RevID: ID of Receiving task.
 * @pMsg: Pointer to the message.
 * @return: TURE if successful, otherwise FALSE;
 *
 * @notes:
 */
bool_t mboxSend(mail_t *pMail, uword_t Key, word_t RevID, void *pMsg) {
    msg_t *pmark = NULL;
    uword_t id;

    if((pMail == NULL) || (pMail->start == NULL) || (pMsg == NULL)) {
        return(FALSE);
    }

    mac_disable_irq();
    if(pMail->size == pMail->used) {
        mac_enable_irq();
        return(FALSE);
    }

    pmark = pMail->pos;
    do {
        if(pMail->pos->pmsg == NULL) {
            break;
        } else if(pMail->pos++ == pMail->end) {
            pMail->pos = pMail->start;
        }
    } while(pMail->pos != pmark);

    pMail->pos->key_id = Key;
    id = current()->id;
    pMail->pos->send_id = id;
    pMail->pos->rev_id = RevID;
    pMail->pos->pmsg = pMsg;
    pMail->used++;
    mac_enable_irq();
    return(TRUE);
}



/**
 * mboxRev - Retrieve message from mailbox.
 * @pMail: Pointer to the mailbox.
 * @Key: Serial number of message.
 * @SendID: ID of sending task.
 * @return: The message if successful, NULL for failed.
 *
 * @notes:
 */
void *mboxRev(mail_t *pMail, uword_t Key, word_t SendID) {
    msg_t *pmark = NULL;
    void *remsg = NULL;

    if((pMail == NULL) || (pMail->start == NULL)) {
        return(NULL);
    }

    mac_disable_irq();
    if(pMail->used == 0) {
        mac_enable_irq();
        return(NULL);
    }

    /* check message */
    pmark = pMail->pos;
    do {
        if((pMail->pos->pmsg != NULL) &&  \
           (pMail->pos->key_id == Key) &&  \
           (pMail->pos->send_id == SendID) &&  \
           (pMail->pos->rev_id == current()->id)) {

            remsg = pMail->pos->pmsg;

            pMail->pos->key_id = 0;
            pMail->pos->send_id = 0;
            pMail->pos->rev_id = 0;
            pMail->pos->pmsg = NULL;

            pMail->used--;
            mac_enable_irq();
            return(remsg);
        } else if(pMail->pos++ == pMail->end) {
            pMail->pos = pMail->start;
        }
    } while(pMail->pos != pmark);

    mac_enable_irq();
    return(NULL);
}



/**
 * mboxGetUsed - Get current used space of mailbox.
 * @pMail: Pointer to the mailbox.
 * @return:
 *
 * @notes:
 */
word_t mboxGetUsed(const mail_t *pMail) {
    if((pMail == NULL) || (pMail->start == NULL)) {
        return(-1);
    }
    return pMail->used;
}



/**
 * mboxClear - Clear a mailbox.
 * @pMail: Pointer to the mailbox.
 * @return:
 *
 * @notes:
 */
void mboxClear(mail_t *pMail) {
    if((pMail != NULL) && (pMail->start != NULL)) {
        pMail->used = 0;
        pMail->pos = pMail->start;
    }
}



/**
 * mboxDel - Delete a mailbox.
 * @pMail: Pointer to the mailbox.
 * @return:
 *
 * @notes:
 */
void mboxDel(mail_t *pMail) {
    if((pMail != NULL) && (pMail->start != NULL)) {
        kfree(pMail->start);
        pMail->start = NULL;
        kfree(pMail);
    }
}

